#include <pcap.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <iostream>
#include <cstring>
#include <thread>
#include <queue>
#include <unistd.h>
#include <sys/stat.h>
#include  <memory> // 共享指针必须要包含的头文件

#include "log4cpp/Category.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/SimpleLayout.hh"

#include "pkg_grab.hpp"
#include "pkg_info.hpp"
#include "my_queue.hpp"
using namespace std;
// template<class T> class MessageQueue;

static MessageQueue<PKG_INFO> pkg_info_q;


int parse_http_header(char *data, int data_len, char *path) {
    char *start = strstr(data, "GET");
    if (start == NULL) {
        start = strstr(data, "POST");
    }
    if (start == NULL) {
        // 不是 HTTP 请求，不处理
        return -1;
    }

    // 解析出 PATH
    char *end = strstr(start, " HTTP/1.");
    if (end == NULL) {
        // 不是 HTTP 请求，不处理
        return -2;
    }
    int path_len = end - start - 4;
    strncpy(path, start + 4, path_len);
    path[path_len] = '\0';
    
    return strlen(path);
}


void log_init(string net_name)
{
    PKG_INFO pkg_info;
    char last_time[20] = {0};
    char file_name[128];
    log4cpp::Appender *appender = nullptr;
    // layout = nullptr;
    strcpy(last_time, "2020-01-01 10");
//创建日志文件路径
    if(access("/home/logs_file", 0) != 0)
    {
        mkdir("/home/logs_file", 0777);
    }

    log4cpp::Appender *osappender = new log4cpp::OstreamAppender("OstreamAppender", &std::cout);
    log4cpp::Layout *layout = new log4cpp::SimpleLayout();   // 有不同的layout

    log4cpp::Category &warn_log =
    log4cpp::Category::getInstance("lsy");   // 是一个单例工厂
    warn_log.addAppender(osappender);

    warn_log.setPriority(log4cpp::Priority::INFO);

    do {
        pkg_info_q.wait(pkg_info);
        u_char *p_data = pkg_info.data;
    
        struct ethhdr *ethhdr_info = (struct ethhdr*) p_data;
        struct ip_header *ip_hdr = (struct ip_header *) (p_data + sizeof(struct ethhdr));
        struct tcp_header *tcp_hdr = (struct tcp_header *) (p_data + sizeof(struct ip_header) + sizeof(struct ethhdr));
        int data_len = ntohs(ip_hdr->ip_len) - sizeof(struct ip_header) - tcp_hdr->th_offx2/4;
        if (data_len <= 1) {
            // 没有数据，不处理
            continue;
        }
        char tcp_time[NOW_TIME_LENGTH];
        char sour_ip[IP_LENGTH];
        char dest_ip[IP_LENGTH];
        char web_path[PATH_LENGTH];
        struct tm info;
        localtime_r(&pkg_info.tcp_time, &info);
        //时间格式，文件名从中截取前置字符
        strftime(tcp_time, NOW_TIME_LENGTH, "%Y-%m-%d %H:%M:%S", &info);
        if(memcmp(last_time, tcp_time, strlen(last_time)) != 0)
        {
            strncpy(last_time, tcp_time, strlen(last_time));
            
            snprintf(file_name, sizeof(file_name), "/home/logs_file/%s_%s.log", net_name.c_str(), tcp_time);
	        printf("create log file:%s\n", file_name);
            if(appender != nullptr)
            {
		appender->close();
                warn_log.removeAppender(appender);
                // delete appender;
                // delete layout;
            }
            printf("1\n");
            appender = new log4cpp::FileAppender("FileAppender", file_name);
            if (appender == nullptr)
            {
                warn_log.alert("内存用光了");
                // clean up and flush all appenders
                log4cpp::Category::shutdown();
                exit(-1);
            }
            printf("2\n");
            appender->setLayout(layout);
            
            warn_log.setAppender(appender);
        }
        strcpy(sour_ip, inet_ntoa(ip_hdr->ip_src));
        strcpy(dest_ip, inet_ntoa(ip_hdr->ip_dst));

        char *data = (char *) (p_data + sizeof(struct ip_header) + sizeof(struct ethhdr) + tcp_hdr->th_offx2/4);

        int err_code = parse_http_header(data, data_len, web_path);
        if(err_code <= 0)
        {
            
        }
        else
        {
            warn_log.info("%s\t%s\t%s\t%s\t", tcp_time, sour_ip, dest_ip, web_path);
        }
    }while(1);
    // clean up and flush all appenders
    log4cpp::Category::shutdown();
}



void handle_packet(u_char *user, const struct pcap_pkthdr *header, const u_char *pkt_data) {
    // 解析 IP 头部
    struct ethhdr *ethhdr_info = (struct ethhdr*) pkt_data;
    struct ip_header *ip_hdr = (struct ip_header *) (pkt_data + sizeof(struct ethhdr));
    PKG_INFO pkg_info;
    if (ip_hdr->ip_p != IPPROTO_TCP) {
        // 不是 TCP 协议，不处理
        return;
    }

    // 解析 TCP 头部
    struct tcp_header *tcp_hdr = (struct tcp_header *) (pkt_data + sizeof(struct ip_header) + sizeof(struct ethhdr));
    if (ntohs(tcp_hdr->th_dport) != HTTP_PORT) {
        // 不是 HTTP 请求，不处理
        return;
    }
    
    pkg_info.data_len = sizeof(struct ethhdr) + ntohs(ip_hdr->ip_len);
    pkg_info.tcp_time = time(nullptr);
    memcpy(pkg_info.data, pkt_data, pkg_info.data_len);
    // pkg_info.data = make_shared <u_char[]>(pkg_info.data_len);
    // pkg_info.data = new(u_char[pkg_info.data_len]); 
    // memcpy(pkg_info.data.get(), pkt_data, pkg_info.data_len);
    
    pkg_info_q.push(pkg_info);

}

#if 1
int main(int argc, char *argv[]) {
    char errbuf[PCAP_ERRBUF_SIZE];

    if(argc != 2){
        std::cout << "usage: " << std::endl;
        std::cout << "net_grab eth0"  << std::endl;
        return -1;
    }

    thread log_thread(log_init, argv[1]);


    pcap_t *handle = pcap_open_live(argv[1], BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL) {
        cerr << "Error opening device: " << errbuf << endl;
        return -1;
    }

    if (pcap_datalink(handle) != DLT_EN10MB) {
        cerr << "Device not Ethernet" << endl;
        return -1;
    }

    struct bpf_program fp;
    char filter_exp[] = "dst port 80";
    // char filter_exp[] = "";
    if (pcap_compile(handle, &fp, filter_exp, 0, PCAP_NETMASK_UNKNOWN) == -1) {
        cerr << "Error compiling filter" << endl;
        return -1;
    }

    if (pcap_setfilter(handle, &fp) == -1) {
        cerr << "Error setting filter" << endl;
        return -1;
    }


    pcap_loop(handle, -1, handle_packet, NULL);

    pcap_freecode(&fp);
    pcap_close(handle);

    log_thread.join();
    return 0;
}
#else

#endif
